package com.vlefen.springcloud.grpc.spring;

import com.vlefen.springcloud.grpc.GRpc;
import com.vlefen.springcloud.grpc.grpc.GRpcType;
import com.vlefen.springcloud.grpc.grpc.StubFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationContext;
import org.springframework.core.PriorityOrdered;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;

public class GRpcAutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryPostProcessor, PriorityOrdered {

    private final ApplicationContext applicationContext;
    private final StubFactory stubFactory;
    private ConfigurableListableBeanFactory beanFactory;
    private final int order = 2147483644;


    public GRpcAutowiredAnnotationBeanPostProcessor(ApplicationContext applicationContext, StubFactory stubFactory) {
        this.applicationContext = applicationContext;
        this.stubFactory = stubFactory;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        this.beanFactory = configurableListableBeanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass.isInterface()) {
            return super.postProcessBeforeInstantiation(beanClass, beanName);
        }

        ReflectionUtils.doWithLocalFields(beanClass, (field -> {
            GRpc gRpc = field.getAnnotation(GRpc.class);
            if (null != gRpc) {
                resolverDependency(field.getType().getName(), gRpc);
            }
        }));

        Constructor<?>[] constructors = beanClass.getDeclaredConstructors();
        if (null == constructors || constructors.length == 0) {
            return constructors;
        }
        for (Constructor<?> constructor : constructors) {
            Parameter[] parameters = constructor.getParameters();
            if (null == parameters || parameters.length == 0) {
                continue;
            }

            for (Parameter parameter : parameters) {
                if (!parameter.isAnnotationPresent(GRpc.class)) {
                    continue;
                }
                GRpc grpc = parameter.getAnnotation(GRpc.class);
                resolverDependency(parameter.getType().getName(), grpc);
            }
        }

        return super.postProcessBeforeInstantiation(beanClass, beanName);
    }


    private void resolverDependency(String name, GRpc grpc) {
        if (!applicationContext.containsBean(name)) {
            DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
            Class serviceDefinition = grpc.serviceDefinition();
            GRpcType type = grpc.type();
            try {
                Object o = stubFactory.createStub(serviceDefinition, type, discoveryClient);
                beanFactory.registerSingleton(name, o);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }

        }
    }


    @Override
    public int getOrder() {
        return order;
    }
}
